/*
 * dinput8-nodevices - A dummy DirectInput8 library that returns no devices
 * Copyright (c) 2014, Jack Partridge, All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3.0 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.
 */

#include "stdafx.h"
#define INITGUID
#include <dinput.h>

class CDirectInput8 : public IDirectInput8
{
public:
	CDirectInput8(/*IDirectInput8 *pInterface*/)// : m_pInterface(pInterface)
	{
		m_cRef = 0;
	}

	STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj)
	{
		    // Always set out parameter to NULL, validating it first.
			if (!ppvObj)
				return E_INVALIDARG;
			*ppvObj = NULL;
			if (riid == IID_IUnknown || riid == IID_IDirectInput8W || riid == IID_IDirectInput8A)
			{
				// Increment the reference count and return the pointer.
				*ppvObj = (LPVOID)this;
				AddRef();
				return NOERROR;
			}
			return E_NOINTERFACE;
	}

	STDMETHOD_(ULONG,AddRef)(THIS)
	{
		InterlockedIncrement(&m_cRef);
		return m_cRef;
	}

	STDMETHOD_(ULONG,Release)(THIS)
	{
		// Decrement the object's internal counter.
		ULONG ulRefCount = InterlockedDecrement(&m_cRef);
		if (0 <= m_cRef)
		{
			//delete this;

			// Static class, don't delete
			InterlockedExchange(&m_cRef, 0);
		}
		return ulRefCount;
	}

	/*** IDirectInput8W methods ***/
	STDMETHOD(CreateDevice)(THIS_ REFGUID rguid, LPDIRECTINPUTDEVICE8W * lplpDirectInputDevice, LPUNKNOWN pUnkOuter)
	{
		return DIERR_DEVICENOTREG;
	}

	STDMETHOD(EnumDevices)(THIS_ DWORD dwDevType, LPDIENUMDEVICESCALLBACK lpCallback, LPVOID pvRef, DWORD dwFlags)
	{
		return DI_OK;
	}

	STDMETHOD(GetDeviceStatus)(THIS_ REFGUID rguidInstance)
	{
		return DI_NOTATTACHED;
	}

	STDMETHOD(RunControlPanel)(THIS_ HWND hwndOwner,DWORD dwFalgs)
	{
		return DI_OK;
	}

	STDMETHOD(Initialize)(THIS_ HINSTANCE hinst, DWORD dwVersion)
	{
		return DI_OK;
	}

	STDMETHOD(FindDevice)(THIS_ REFGUID rguidClass, LPCTSTR ptszName, LPGUID pguidInstance)
	{
		return DIERR_DEVICENOTREG;
	}

	STDMETHOD(EnumDevicesBySemantics)(THIS_ LPCTSTR ptszUserName, LPDIACTIONFORMAT lpdiActionFormat, LPDIENUMDEVICESBYSEMANTICSCB lpCallback, LPVOID pvRef, DWORD dwFlags)
	{
		return DI_OK;
	}

	STDMETHOD(ConfigureDevices)(THIS_ LPDICONFIGUREDEVICESCALLBACK lpdiCallback, LPDICONFIGUREDEVICESPARAMS lpdiCDParams, DWORD dwFlags, LPVOID pvRefData)
	{
		return DI_OK;
	}

private:
	//IDirectInput8 *m_pInterface;
	volatile LONG m_cRef;
};

static CDirectInput8 s_DirectInput8;

/*void GetDInputDllPath(wchar_t *buf, size_t size)
{
	wchar_t const *dll_filename = L"\\dinput8.dll";
	GetSystemDirectoryW(buf, size);
	wcscat_s(buf, size, dll_filename);
}*/

//typedef HRESULT (WINAPI *DirectInput8Create_t)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter);

HRESULT WINAPI DirectInput8Create(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID * ppvOut, LPUNKNOWN punkOuter)
{
	return s_DirectInput8.QueryInterface(riidltf, ppvOut);
}

class DI8ClassFactory : public IClassFactory
{
public:
	virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject)
	{
		return E_NOINTERFACE;
	}

	virtual ULONG STDMETHODCALLTYPE AddRef(void)
	{
		InterlockedIncrement(&m_cRef);
		return m_cRef;
	}

	virtual ULONG STDMETHODCALLTYPE Release(void)
	{
		ULONG ulRefCount = InterlockedDecrement(&m_cRef);
		if (0 <= m_cRef)
		{
			// Static class, don't delete
			InterlockedExchange(&m_cRef, 0);
		}
		return ulRefCount;
	}

	virtual HRESULT STDMETHODCALLTYPE CreateInstance(  IUnknown *pUnkOuter, REFIID riid, void **ppvObject)
	{
		if( IsEqualGUID( IID_IDirectInput8A, riid ) || IsEqualGUID( IID_IDirectInput8W, riid ) || IsEqualGUID( IID_IUnknown, riid ))
		{
			IDirectInput *ppDI;
			HRESULT hr = s_DirectInput8.QueryInterface(riid, (void **)&ppDI);
			ppDI->Release();

			*ppvObject = ppDI;
 
			return hr;
		}
 
		return E_NOINTERFACE;
	}

	virtual HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock)
	{
		if(fLock)
		   AddRef();
	   else
		   Release();
 
		return S_OK;
	}

	bool CanUnload()
	{
		return (m_cRef == 0);
	}

private:
	LONG m_cRef;
};

static DI8ClassFactory s_DI8ClassFactory;

HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
    if ( IsEqualCLSID( IID_IClassFactory, riid ) ) {
        *ppv = &s_DI8ClassFactory;
        s_DI8ClassFactory.AddRef();
        return S_OK;
    }

    return CLASS_E_CLASSNOTAVAILABLE;
}

HRESULT WINAPI DllCanUnloadNow(void)
{
    return s_DI8ClassFactory.CanUnload();
}

HRESULT WINAPI DllRegisterServer(void)
{
    return S_OK;
}

HRESULT WINAPI DllUnregisterServer(void)
{
    return S_OK;
}